package com.dts.gateway.config;

import com.alibaba.fastjson.JSON;
import com.dts.gateway.dto.ApiResult;
import com.dts.gateway.enums.ResultEnum;
import com.dts.gateway.util.IpAdrressUtil;
import com.dts.gateway.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

/**
 * <p>
 *  过滤器: 实现接口限流，因网关使用SpringMVC拦截器会启动报错，所以改造成过滤器实现
 *  原理：代码中设置的是1S内同一个接口通过同一个ip访问5次，就将该ip禁用1分钟
 * </p>
 *
 * @author zhoujh
 * @date 2022/06/09 10:55
 */
@Slf4j
@Component
public class IpUrlLimitFilter implements GlobalFilter, Ordered {
    @Resource
    private RedisUtil redisUtil;

    private static final String LOCK_IP_URL_KEY = "lock_ip_";

    private static final String IP_URL_REQ_TIME = "ip_url_times_";

    private static final long LIMIT_TIMES = 5;

    private static final int IP_LOCK_TIME = 60;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
//        ServerHttpResponse response = exchange.getResponse();
//        HttpServletResponse httpServletResponse = response.getServletResponse();
        log.info("request请求地址uri={},ip={}", request.getRemoteAddress(), IpAdrressUtil.getIpAdrress(request));
        try {
            if (ipIsLock(IpAdrressUtil.getIpAdrress(request))) {
                log.info("ip访问被禁止={}", IpAdrressUtil.getIpAdrress(request));
                ApiResult result = ApiResult.call(ResultEnum.LOCK_IP);
                //returnJson(response, JSON.toJSONString(result));
                throw new RuntimeException(JSON.toJSONString(result));
            }
            if (!addRequestTime(IpAdrressUtil.getIpAdrress(request), request.getURI().getHost())) {
                ApiResult result = ApiResult.call(ResultEnum.LOCK_IP);
                //returnJson(response, JSON.toJSONString(result));
                throw new RuntimeException(JSON.toJSONString(result));
            }
            return chain.filter(exchange);
        } catch (Exception e) {
            log.error("过滤器出现出错：", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public int getOrder() {
        return -1;
    }

    /**
     * 判断ip是否被禁用
     *
     * @param ip
     * @return java.lang.Boolean
     * @Description: 判断ip是否被禁用
     */
    private Boolean ipIsLock(String ip) {
        // RedisUtil redisUtil = getRedisUtil();
        if (redisUtil.hasKey(LOCK_IP_URL_KEY + ip)) {
            return true;
        }
        return false;
    }

    /**
     * 记录请求次数
     *
     * @param ip  ip地址
     * @param uri 绝对地址
     * @return java.lang.Boolean
     */
    private Boolean addRequestTime(String ip, String uri) {
        // 去除+ uri
        String key = IP_URL_REQ_TIME + ip;
        //RedisUtil redisUtil = getRedisUtil();
        if (redisUtil.hasKey(key)) {
            long time = redisUtil.incr(key,  1);
            if (time >= LIMIT_TIMES) {
                redisUtil.getLock(LOCK_IP_URL_KEY + ip, ip, IP_LOCK_TIME);
                return false;
            }
        } else {
            redisUtil.getLock(key, "1", 1);
        }
        return true;
    }
    //浏览器需要print输出
/*    private void returnJson(ServerHttpResponse response, String json) throws Exception {
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/json; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(json);
        } catch (IOException e) {
            log.error("LoginInterceptor response error ---> {}", e.getMessage(), e);
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }*/
}
